"use strict";

/**
 * 创建回到顶部按钮插件
 * 功能：在页面滚动到一定位置时显示按钮，点击可平滑滚动到顶部
 * 支持配置定位基准（顶部或底部），并为不同基准提供合理默认距离
 * @param {Object} params - 配置参数对象
 */
function addBackToTop(params = {}) {
  // 解构并设置默认参数
  const {
    id = 'back-to-top',               // 按钮元素ID
    zIndex = 1000,                    // 元素层级

    showWhenScrollTopIs = 1,          // 滚动超过此像素值时显示按钮(px)
    scrollDuration = 100,             // 滚动动画持续时间(ms)
    onClickScrollTo = 0,              // 点击后滚动到的位置(px)或返回数值的函数

    backgroundColor = '#000',         // 按钮背景色
    textColor = '#fff',               // 按钮文本/图标颜色
    // 按钮内部HTML(默认使用向上箭头SVG图标)
    innerHTML = '<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></svg>',
    diameter = 56,                    // 按钮直径(px)
    size = diameter,                  // diameter的别名，用于保持兼容性

    // 定位配置
    positionFrom = 'bottom',          // 定位基准：'top'（顶部）或 'bottom'（底部）
    topDistance = 520,                 // 相对于顶部定位时的距离(px)
    bottomDistance = 180,              // 相对于底部定位时的距离(px)
    right = 30,                       // 距离右侧的距离(px)

    // 动画缓动函数，默认使用正弦曲线缓动
    ease = inOutSine,
    // 滚动容器，默认为body
    scrollContainer = document.body,
  } = params;

  // 判断滚动容器是否为body
  const scrollContainerIsBody = scrollContainer === document.body;
  // 获取文档元素(用于兼容性处理)
  const scrollDocumentElement = scrollContainerIsBody && document.documentElement;

  // 追加样式到页面
  appendStyles();
  // 创建并添加按钮元素到页面
  const upEl = appendElement();
  // 按钮初始状态为隐藏
  let hidden = true;

  // 确定滚动事件的监听对象
  const scrollEmitter = scrollContainerIsBody ? window : scrollContainer;
  // 监听滚动事件，动态显示/隐藏按钮
  scrollEmitter.addEventListener('scroll', adapt);
  // 初始化时检查一次状态
  adapt();

  /**
   * 根据滚动位置调整按钮显示状态
   */
  function adapt() {
    if (getScrollTop() >= showWhenScrollTopIs) {
      show();
    } else {
      hide();
    }
  }

  /**
   * 显示回到顶部按钮
   */
  function show() {
    if (!hidden) return;
    upEl.className = '';
    hidden = false;
  }

  /**
   * 隐藏回到顶部按钮
   */
  function hide() {
    if (hidden) return;
    upEl.className = 'hidden';
    hidden = true;
  }

  /**
   * 创建并添加按钮元素到页面
   * @returns {HTMLElement} 创建的按钮元素
   */
  function appendElement() {
    const upEl = document.createElement('div');
    upEl.id = id;
    upEl.className = 'hidden'; // 初始隐藏
    upEl.innerHTML = innerHTML;

    // 绑定点击事件，点击时滚动到顶部
    upEl.addEventListener('click', event => {
      event.preventDefault();
      scrollUp();
    });

    document.body.appendChild(upEl);
    return upEl;
  }

  /**
   * 动态创建并添加样式到页面
   */
  function appendStyles() {
    // 计算SVG图标尺寸和位置（基于按钮大小的比例）
    const svgSize = Math.round(0.43 * size);
    const svgTop = Math.round(0.29 * size);

    // 根据定位基准确定位置样式
    const positionStyle = positionFrom === 'top'
      ? `top: ${topDistance}px`
      : `bottom: ${bottomDistance}px`;

    // 样式定义
    const styles = /*css*/`
      #${id} {
        background: ${backgroundColor};
        border-radius: 50%;
        ${positionStyle}; /* 应用计算后的定位样式 */
        box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26);
        color: ${textColor};
        cursor: pointer;
        display: block;
        height: ${size}px;
        opacity: 1;
        outline: none;
        position: fixed;
        right: ${right}px;
        -webkit-tap-highlight-color: transparent;
        -webkit-touch-callout: none;
        user-select: none;
        width: ${size}px;
        z-index: ${zIndex};
      }
      #${id} svg {
        display: block;
        fill: currentColor;
        height: ${svgSize}px;
        margin: ${svgTop}px auto 0;
        width: ${svgSize}px;
      }
      #${id}.hidden {
        ${positionFrom === 'top' 
          ? `top: -${size}px`  // 顶部定位时隐藏到视口上方
          : `bottom: -${size}px`}; // 底部定位时隐藏到视口下方
      }
    `;

    // 创建并添加样式元素到头部
    const styleEl = document.createElement('style');
    styleEl.appendChild(document.createTextNode(styles));
    document.head.insertAdjacentElement('afterbegin', styleEl);
  }

  /**
   * 平滑滚动到顶部或指定位置
   */
  function scrollUp() {
    // 确定滚动目标位置
    const scrollTo = typeof onClickScrollTo === 'function'
      ? onClickScrollTo()
      : onClickScrollTo;

    // 获取浏览器性能API和动画API
    const { performance, requestAnimationFrame } = window;

    // 不支持动画API或无需动画时，直接设置滚动位置
    if (scrollDuration <= 0 ||
        typeof performance === 'undefined' ||
        typeof requestAnimationFrame === 'undefined') {
      return setScrollTop(scrollTo);
    }

    // 动画开始时间
    const start = performance.now();
    // 当前滚动位置
    const initScrollTop = getScrollTop();
    // 需要滚动的距离
    const pxsToScrollBy = initScrollTop - scrollTo;

    // 开始动画帧
    requestAnimationFrame(step);

    /**
     * 动画帧处理函数
     * @param {number} timestamp - 时间戳
     */
    function step(timestamp) {
      // 计算动画进度(0-1)
      const progress = Math.min((timestamp - start) / scrollDuration, 1);
      // 根据缓动函数计算当前滚动位置并设置
      setScrollTop(initScrollTop - Math.round(ease(progress) * pxsToScrollBy));
      // 动画未完成时继续请求下一帧
      if (progress < 1) {
        requestAnimationFrame(step);
      }
    }
  }

  /**
   * 获取当前滚动位置
   * @returns {number} 滚动位置像素值
   */
  function getScrollTop() {
    return scrollContainer.scrollTop ||
           (scrollDocumentElement && document.documentElement.scrollTop) ||
           0;
  }

  /**
   * 设置滚动位置
   * @param {number} value - 目标滚动位置像素值
   */
  function setScrollTop(value) {
    scrollContainer.scrollTop = value;
    // 兼容性处理：同时设置documentElement的scrollTop
    if (scrollDocumentElement) {
      document.documentElement.scrollTop = value;
    }
  }

  /**
   * 正弦曲线缓动函数(进入和退出都使用正弦曲线)
   * @param {number} n - 进度值(0-1)
   * @returns {number} 缓动后的进度值
   */
  function inOutSine(n) {
    return 0.5 * (1 - Math.cos(Math.PI * n));
  }
}
